<?php

namespace yi\di;

use ReflectionClass;
use yi\base\Component;

class Container extends Component
{
    /**
     * @var array object definitions indexed by their types
     */
    private $_definitions = [];
    private $_reflections = [];
    private $_dependencies = [];

    public function get($class, $params = [], $config = [])
    {
        if (!isset($this->_definitions[$class])) {
            return $this->build($class, $params, $config);
        }
    }


    protected function build($class, $params, $config)
    {
        /* @var $reflection ReflectionClass */
        list($reflection, $dependencies) = $this->getDependencies($class);
        $addDependencies = [];
        foreach ($params as $index => $param) {
            $addDependencies[$index] = $param;
        }

        $dependencies = array_values($dependencies);
        $dependencies = $this->mergeDependencies($dependencies, $addDependencies);
        $object = $reflection->newInstanceArgs($dependencies);
        if (empty($config)) {
            foreach ($config as $name => $value) {
                $object->$name = $value;
            }
        }
        return $object;
    }


    /**
     * @param array $a
     * @param array $b
     * @return array
     */
    private function mergeDependencies($a, $b)
    {
        foreach ($b as $index => $dependency) {
            $a[$index] = $dependency;
        }
        return $a;
    }


    protected function getDependencies($class)
    {
        $dependencies = [];
        try {
            $reflection = new ReflectionClass($class);
        } catch (\ReflectionException $e) {
            throw new \Exception('Failed to instantiate component or class "' . $class);
        }

        $constructor = $reflection->getConstructor();
        if ($constructor !== null) {
            foreach ($constructor->getParameters() as $param) {
                $dependencies[$param->getName()] = $param->isDefaultValueAvailable()
                    ? $param->getDefaultValue()
                    : null;
            }
        }

        $this->_reflections[$class] = $reflection;
        $this->_dependencies[$class] = $dependencies;

        return [$reflection, $dependencies];
    }
}